package com.cskaoyan.javase.oop1._5constructor;

/**
 * @description: 构造方法的使用
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * > 创建一个教师类，有课程和年龄两个属性，行为是可以上课
 * - 思考一下，我们给成员变量赋值的语句，是不是显得冗余？
 * - 怎么改进？
 * - 对象的属性，能不能"出厂"的时候就设定好呢？
 *
 * 实际上是可以的,考虑以下:
 * 1,直接显式赋值,这样可以大致实现功能,但是显然不够灵活,这种方式不推荐使用
 * 2,使用构造方法给成员变量赋值
 *
 * 构造器(构造方法/构造函数/constructor):
 *  指的是类当中的一种特殊的方法,它的语法和普通的方法完全不一样,它的调用也具有独特性
 * 语法:
 *  [访问权限修饰符] 类名(参数列表){
 * 	    //方法体
 *  }
 * [访问权限修饰符]不是必须的,完全可以不写
 * 类名"可以"认为是构造器的名字,它应当和当前类的类名保持完全一致
 * 参数列表可以为空,也可以不空
 * 构造方法的方法体有标准的书写格式,不是乱写的(当然如果你是瞎创建了一个构造方法,乱写是可以的)
 *
 * 构造器的用途:
 *      在Java程序设计语言中，new创建对象，必然使用构造器.
 *      构造器不是程序员调用的,而是JVM在创建对象的最后一步自动去调用的,程序员可以指示JVM选择何种构造器,但是无权调用
 *      构造器的终极目的是为了给成员变量赋值,并且为了保证赋值的准确性,构造器永远在最后一步执行
 * 以上,给成员变量赋值,不管有何种花里胡哨的方式,永远记住以下两点:
 *      1,永远先进行默认初始化具有默认值
 *      2,构造器永远最后执行(你可以永远相信构造器)
 *      我称这个原则是"掐头去尾" 头是默认值,尾是构造器赋值
 *
 * 构造器的使用:
 *      程序员可以指示JVM选择何种构造器,但是无权调用,指示的方式是: new 类名(参数) 指示jvm选择何种构造器
 * 注意事项:
 *      1,java当中的一个类,如果没有明确给出一个构造器,那么隐式的提供默认无参构造方法
 *          如果已经明确给出了任何一个构造方法,默认无参构造方法就不再提供
 *          public 类名(){}
 *          因为Java的这个特点,建议大家在定义一个可能创建对象的类时,先手写上默认无参,避免造成一些错误和问题
 *          IDEA自动生成构造方法的快捷键: alt + insert 找到其中constructor,然后生成构造方法
 *
 *      2,无参构造方法无法完成成员变量赋值,需要有参的构造方法
 *      3,像以下格式书写的构造器称之为标准格式构造器,标准格式构造器赋值一定是准确的
 *      public Teacher(){}
 *      public Teacher(String course,int age){
 *         this.course = course;
 *         this.age = age;
 *      }
 *
 *      4,一个类可以有多个构造方法，不同的构造方法之间遵循“方法重载”的原则，也就是说它们必须形参列表不同。比如同时给两个相同数据类型的成员变量赋值，这是做不到的！
 *
 *      5,构造方法中也存在隐含的传参this,this表示当前对象,这个时候它的作用和成员方法中的this作用是一致的
 *      比如可以区分同名成员变量和形参
 *      还可以使用this.访问成员变量和成员方法(可省略this)
 *
 *      6,构造器中的this有自己独特的用途: 可以使用this去调用同类的其它构造方法,语法: this(参数)
 *      根据参数来分辨调用哪个构造方法
 *      需要注意在构造器中使用this调用别的构造器,该条语句必须在第一行(也就是说只能使用一次)
 *
 *      7,我们知道new对象需要去调用构造方法,new对象之前需要进行类加载,
 *          类加载只有一次,而new对象一次构造器就会执行一次
 *
 *      8,总结到目前为止给成员变量赋值的方式:
 *          1,默认初始化具有默认值
 *          2,显式赋值
 *          3,构造器赋值
 *      赋值的顺序是: 默认初始化最先,构造器在最后,显式赋值在中间
 *
 *
 */
public class Demo {
    public static void main(String[] args) {
        /*
        无参构造
        //创建教师对象
        //1,教师1: 18岁教Java
        Teacher t = new Teacher();
        t.age = 18;
        t.course = "Java";
        //2,教师2: 30岁教C++
        Teacher t2 = new Teacher();
        t.age = 30;
        t.course = "C++";
        //3,教师3: 50岁教Python
        Teacher t3 = new Teacher();
        t.age = 50;
        t.course = "Python";*/

        /*Teacher t = new Teacher("Java", 18);
        System.out.println(t.age); //18
        System.out.println(t.course); //Java*/

        //使用默认无参创建对象
        //Teacher t2 = new Teacher();

        Teacher t = new Teacher();
        Teacher t2 = new Teacher();
        Teacher t3 = new Teacher();
        Teacher t4 = new Teacher();

    }
}

class Teacher {
    String course = "zzz";
    int age = 98;
    String name = "马云";

    public void teach() {
        System.out.println("老师在催眠!");
    }


    //默认无参构造方法的格式
    public Teacher() {
        System.out.println("hello world!");
    }

    //有参的构造方法
    //双参数的构造方法
    public Teacher(String course, int age) {
        //使用this区分同名的局部变量和成员变量
        this.course = course;
        this.age = age;
        //teach();
    }

    //单独给name赋值的构造方法
    public Teacher(String name) {
        this.name = name;
    }

    //三参的构造方法
    public Teacher(String course, int age, String name) {

        //使用this调用双参构造器
        this(course, age);
        //Call to 'this()' must be first statement in constructor body
        //this(name);
        this.name = name;
    }

    //形参的名字不同 无法构成方法重载
    //public Teacher(String course){
    //    this.course = course;
    //}

}

class Cat {
    int age;
    String name;
    double price;

    //alt + insert
    public Cat() {
    }

    public Cat(int age) {
        this.age = age;
    }

    public Cat(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public Cat(int age, String name, double price) {
        /*this.age = age;
        this.name = name;*/
        this(age, name);
        this.price = price;
    }
}